package com.example.android.network;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

import com.example.android.synchronization.ThreadEvent;

public class Client implements Runnable {
	private static Client instance = new Client();

	String serverIpAddress;
	int serverPort;
	Socket clientSocket;
	Sender sender;
	Thread senderThread;
	Receiver receiver;
	Thread receiverThread;

	ThreadEvent threadEvent = new ThreadEvent();
	ThreadEvent semaphor = new ThreadEvent();

	private Client() {
	}

	public static Client getInstance() {
		return instance;
	}

	public void send(OutPacketType packetType, Object... objects) {
		StringBuilder packet = new StringBuilder();
		packet.append(packetType.getTranslation());
		for (Object object : objects) {
			packet.append(object.toString());
			packet.append(Constants.valSeparator);
		}
		sender.send(packet.toString());
	}

	public void close() {
		try {
			sender.close();
			receiver.close();
			clientSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void connect(String serverIpAddress, int serverPort) {
		this.serverIpAddress = serverIpAddress;
		this.serverPort = serverPort;
		Thread thread = new Thread(this);
		thread.start();
		semaphor.await();
	}

	@Override
	public void run() {
		InetAddress serverAddr;
		try {
			serverAddr = InetAddress.getByName(serverIpAddress);
			SocketAddress address = new InetSocketAddress(serverAddr,
					serverPort);
			clientSocket = new Socket();
			clientSocket.connect(address, 3000);
			sender = new Sender();
			senderThread = new Thread(sender);
			senderThread.start();
			threadEvent.await();
			receiver = new Receiver();
			receiverThread = new Thread(receiver);
			receiverThread.start();
			threadEvent.await();
		} catch (IOException e) {
			e.printStackTrace();
		}
		semaphor.signal();
	}

	class Sender implements Runnable {
		ThreadEvent resultsReady = new ThreadEvent();
		String resultPacket;
		PrintWriter out;
		boolean off = false;

		@Override
		public void run() {
			System.err.println("Starting Sender Thread");
			try {
				out = new PrintWriter(clientSocket.getOutputStream(), true);
				threadEvent.signal();
			} catch (IOException e) {
				e.printStackTrace();
			}

			while (true) {
				resultsReady.await();
				if (off) {
					break;
				}
				System.err.println("RRR:  " + resultPacket);
				out.println(resultPacket);
			}

			System.err.println("Closing Sender Thread");
		}

		public void send(String resultPacket) {
			this.resultPacket = resultPacket;
			resultsReady.signal();
		}

		public void close() {
			off = true;
			resultsReady.signal();
			out.close();
		}
	}

	class Receiver implements Runnable {
		BufferedReader in;
		boolean off = false;

		@Override
		public void run() {
			System.err.println("Starting Receiver Thread");
			try {
				in = new BufferedReader(new InputStreamReader(
						clientSocket.getInputStream()));
				threadEvent.signal();

				while (true) {
					if (off)
						break;
					String packet = in.readLine();
					if (packet != null) {
						ActionFactory.execute(packet);
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			System.err.println("Closing Receiver Thread");
		}

		public void close() {
			off = true;
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
